package com.ruoyi.asset.sorting.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.asset.common.scopecode.constant.ServiceCode;
import com.ruoyi.asset.common.scopecode.service.IScopeCodeService;
import com.ruoyi.asset.homepage.constant.CommunityConstant;
import com.ruoyi.asset.homepage.constant.ServiceTypeConstant;
import com.ruoyi.asset.sorting.constant.SortingDirtConstant;
import com.ruoyi.asset.sorting.domain.GarbageSorting;
import com.ruoyi.asset.sorting.domain.bo.*;
import com.ruoyi.asset.sorting.domain.vo.GarbageSortingVo;
import com.ruoyi.asset.sorting.domain.vo.SortingCommunityCountVo;
import com.ruoyi.asset.sorting.domain.vo.SortingCommunityMapVo;
import com.ruoyi.asset.sorting.domain.vo.SortingWorkStatisticsVo;
import com.ruoyi.asset.sorting.mapper.GarbageSortingMapper;
import com.ruoyi.asset.sorting.service.IGarbageSortingService;
import com.ruoyi.asset.tool.domain.bo.ToolStatisticsBo;
import com.ruoyi.asset.tool.service.IToolService;
import com.ruoyi.asset.workman.service.IWorkmanService;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.tools.excel.MyExcelUtil;
import com.ruoyi.common.tools.excel.base.MyExcelResult;
import com.ruoyi.common.tools.excel.listener.asset.GarbageSortingExcelListener;
import com.ruoyi.common.tools.utils.SerialGenerator;
import com.ruoyi.common.utils.BeanCopyUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.erecord.region.service.IRegionService;
import com.ruoyi.standingbook.sorting.service.ISortingService;
import com.ruoyi.system.service.ISysDictDataService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 垃圾分类督导任务完成进度Service业务层处理
 *
 * @author lq
 * @date 2023-11-30
 */
@RequiredArgsConstructor
@Service
public class GarbageSortingServiceImpl implements IGarbageSortingService {

    private final IToolService toolService;
    private final IWorkmanService workmanService;
    private final GarbageSortingMapper baseMapper;
    private final ISysDictDataService sysDictDataService;
    private final IRegionService regionService;
    private final IScopeCodeService scopeCodeService;
    private final ISortingService sortingService;

    private Boolean batchInsertOrUpdate(List<GarbageSorting> voList) {
		return this.baseMapper.insertOrUpdateBatch(voList);
	}

	@Override
	public List<String> importExcel(String excelUrl) {
		List<String> errorMsgList = new ArrayList<>();

		// 生成批次号
		final String batchNo = SerialGenerator.generator.nextId();

		// excel 处理
		MyExcelResult<GarbageSortingVo> excelResult = MyExcelUtil.getImportExcelData(batchNo, excelUrl,
				new GarbageSortingExcelListener(batchNo), GarbageSortingVo.class, true);

		if (null == excelResult) {
			errorMsgList.add("未获取到要处理的数据");

			return null;
		}

        /**
         *  有解析失败就直接返回错误信息
         */
        if(!excelResult.getErrorList().isEmpty()) {
            return excelResult.getErrorList();
        }

		if (!excelResult.getList().isEmpty()) {
            List<GarbageSorting> list = new ArrayList<>();

            AtomicInteger row = new AtomicInteger(0);

            excelResult.getList().forEach(e -> {
                int currentRow = row.incrementAndGet();
                if (e.getDataCheckResult().isSucess()) {
					GarbageSorting garbageSorting = new GarbageSorting();

					BeanCopyUtils.copy(e, garbageSorting);

					GarbageSorting oldGarbageSorting = getVoByNameAndType(garbageSorting.getSortingPointName(),
							garbageSorting.getSupervisionType());

					if (oldGarbageSorting != null) {
						garbageSorting.setId(oldGarbageSorting.getId());
					} else {
                        String scopeCode = null;

                        try {
                            scopeCode = scopeCodeService.getNextScopeCode(ServiceCode.SORTING,
                                garbageSorting.getSupervisionType(), "");
                        } catch (Exception ex) {
                            errorMsgList.add("获取管理范围编码失败，失败原因：" + ex.getMessage());
                        }

                        garbageSorting.setSortingPointCode(scopeCode);
                    }

                    /**
                     *  如果各行数据校验通过，再遍历检查是否数据重复
                     */
                    if(excelResult.getValidateList().isEmpty()) {
                        AtomicInteger count = new AtomicInteger(0);
                        list.stream().anyMatch(f -> {
                            int currentCount = count.incrementAndGet();
                            if(f.getSortingPointName().equals(garbageSorting.getSortingPointName()) && f.getSupervisionType().equals(garbageSorting.getSupervisionType())) {
                                errorMsgList.add(String.format("第 %d 行数据检查失败，失败原因：与第%d行数据重复", currentRow, currentCount));
                                return true;
                            }
                            return false;
                        });
                    }

					list.add(garbageSorting);
				} else {
					errorMsgList.add(String.format("第 %d 行数据检查失败，失败原因：%s", currentRow, e.getDataCheckResult().getMsg()));
				}
			});

			if (errorMsgList.isEmpty()) {
				batchInsertOrUpdate(list);
			}
		}

		return errorMsgList;
	}

	private GarbageSorting getVoByNameAndType(String sortingPointName, String supervisionType) {
        LambdaQueryWrapper<GarbageSorting> lqw = Wrappers.lambdaQuery();

        lqw.eq(GarbageSorting::getSortingPointName, sortingPointName);
        lqw.eq(GarbageSorting::getSupervisionType, supervisionType);
        lqw.eq(GarbageSorting::getDelFlag, 0);

        List<GarbageSorting> list = baseMapper.selectList(lqw);

        return list.isEmpty() ? null : list.get(0);
	}

	@Override
	public int deleteMaintenanceScope(Set<Long> idsSet) {
		if (CollectionUtils.isEmpty(idsSet)) {
			return -1;
		}

		return baseMapper.logicDeleteMaintenanceScope(idsSet);
	}

	@Override
	public TableDataInfo<GarbageSortingVo> queryMaintenanceScope(GarbageSortingSearchBo searchBo, PageQuery pageQuery) {
		Page<GarbageSortingVo> result = baseMapper.selectMaintenanceScopeWithPage(pageQuery.build(), searchBo);

		return TableDataInfo.build(result);
	}

	@Override
	public List<GarbageSortingVo> queryMaintenanceScope(GarbageSortingSearchBo searchBo) {
		return baseMapper.exportMaintenanceScope(searchBo);
	}

	@Override
	public GarbageSortingOverviewBo getGarbageSortingOverview() {
		GarbageSortingOverviewBo statisticsBo = new GarbageSortingOverviewBo();

		int count = baseMapper.getGarbageSortingCount();
		statisticsBo.setSortingPointCount(count);

        int workCount = workmanService.getByServiceType(ServiceTypeConstant.SORTING);
        statisticsBo.setWorkerCount(workCount);

        ToolStatisticsBo toolStatisticsBo = toolService.getCountByServiceType(ServiceTypeConstant.SORTING);
        if (null != toolStatisticsBo) {
            statisticsBo.setEquipmentCount(toolStatisticsBo.getToolCount());
            statisticsBo.setVehicleCount(toolStatisticsBo.getVehicleCount());
        } else {
            statisticsBo.setEquipmentCount(0);
            statisticsBo.setVehicleCount(0);
        }

		return statisticsBo;
	}

	@Override
	public List<StatisticianSortingByRegion> statForSortingByRegion() {
        List<StatisticianSortingByRegion> list = baseMapper.statForSortingByRegion();
        list.forEach(item->{
			ToolStatisticsBo toolStat = toolService.getCountByServiceTypeAndCommunity(ServiceTypeConstant.SORTING,
					item.getCommunityId());

			item.setEquipmentCount(toolStat.getToolCount());
			item.setVehicleCount(toolStat.getVehicleCount());

			int workCount = workmanService.getCommunityByServiceType(ServiceTypeConstant.SORTING,
					item.getCommunityId());
			item.setWorkerCount(workCount);
		});

		return list;
	}

    @Override
    public Map getGarbageSortingCommunityCount() {
        HashMap<String, GarbageSortingCommunityBo> map = new HashMap<>();
        SysDictData sysDictData = new SysDictData();
        sysDictData.setDictType(CommunityConstant.COMMUNITY_TYPE);
        List<SysDictData> data = sysDictDataService.selectDictDataList(sysDictData);
        for (SysDictData community : data) {
            String communityNo = regionService.getRegionIdByName(community.getDictLabel());
            List<SortingTypeBo> sortingCommunityCount = baseMapper.getGarbageSortingCommunityCount(communityNo);
            sortingCommunityCount.forEach(e->{
                String sortingType = sysDictDataService.selectDictLabel(SortingDirtConstant.SORTING, e.getSupervisionType());
                if (StringUtils.isNotBlank(sortingType)){
                    e.setSupervisionType(sortingType);
                }
            });
            GarbageSortingCommunityBo sortingCommunityBo = new GarbageSortingCommunityBo();
            sortingCommunityBo.setSortingTypeBoList(sortingCommunityCount);
            ToolStatisticsBo toolCount = toolService.getCountByServiceTypeAndCommunity(ServiceTypeConstant.SORTING, communityNo);
            sortingCommunityBo.setToolCount(toolCount.getToolCount());
            sortingCommunityBo.setVehicleCount(toolCount.getVehicleCount());
            int workmanCount = workmanService.getCommunityByServiceType(ServiceTypeConstant.SORTING, communityNo);
            sortingCommunityBo.setWorkmanCommunity(workmanCount);
            map.put(community.getDictLabel(), sortingCommunityBo);
        }

        return map;
    }

    @Override
    public List<SortingCommunityCountVo> getSortingCountByCommunity(String date) {
        SysDictData sysDictData = new SysDictData();
        sysDictData.setDictType(CommunityConstant.COMMUNITY_TYPE);
        List<SysDictData> communities = sysDictDataService.selectDictDataList(sysDictData);
        ArrayList<SortingCommunityCountVo> sortingCommunityCountVos = new ArrayList<>();
        communities.forEach(e->{
            SortingCommunityCountVo countVo = new SortingCommunityCountVo();
            countVo.setCommunity(e.getDictLabel());
            String regionId = regionService.getRegionIdByName(e.getDictLabel());
            Integer numCount = baseMapper.selectSortingCountByCommunity(date, regionId);
            countVo.setNumSum(numCount);
            ToolStatisticsBo toolCount = toolService.getCountByServiceTypeAndCommunity(ServiceTypeConstant.SORTING, regionId);
            if (null != toolCount) {
                countVo.setEquipmentCount(toolCount.getToolCount());
                countVo.setVehicleCount(toolCount.getVehicleCount());
            } else {
                countVo.setEquipmentCount(0);
                countVo.setVehicleCount(0);
            }
            int workmanCount = workmanService.getCommunityByServiceType(ServiceTypeConstant.SORTING, regionId);
            countVo.setWorkerCount(workmanCount);
            sortingCommunityCountVos.add(countVo);
        });
        return sortingCommunityCountVos;
    }

    @Override
    public SortingWorkStatisticsVo getSortingWorkByDate(String date) {
        SortingWorkStatisticsVo statisticsVo = new SortingWorkStatisticsVo();
        SysDictData sysDictData = new SysDictData();
        sysDictData.setDictType(CommunityConstant.COMMUNITY_TYPE);
        List<SysDictData> communities = sysDictDataService.selectDictDataList(sysDictData);
        ArrayList<Integer> numCountList = new ArrayList<>();
        communities.forEach(e->{
            String regionId = regionService.getRegionIdByName(e.getDictLabel());
            Integer numCount = baseMapper.selectSortingCountByCommunity(date, regionId);
            numCountList.add(numCount);
        });

        Integer total = numCountList.stream().reduce(Integer::sum).orElse(0);
        if (total == null) {
            total = 0;
        }
        statisticsVo.setTotalNum(String.valueOf(total));
        Integer sumByDate = sortingService.getSumByDate(date);
        if(sumByDate == null) {
            sumByDate = 0;
        }
        statisticsVo.setFinishedNumSum(String.valueOf(sumByDate));
        if (sumByDate == 0 || total == 0){
            statisticsVo.setPercent(BigDecimal.ZERO);
        } else {
            BigDecimal pec = BigDecimal.valueOf(sumByDate).divide(BigDecimal.valueOf(total),2, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100));
            pec = pec.compareTo(BigDecimal.valueOf(100)) > 0 ? BigDecimal.valueOf(100) : pec;
            statisticsVo.setPercent(pec);
        }
        return statisticsVo;
    }

    @Override
    public SortingWorkStatisticsVo getSortingWorkByDateAndCommunity(String date, String community) {
        SortingWorkStatisticsVo statisticsVo = new SortingWorkStatisticsVo();
        String regionId = regionService.getRegionIdByName(community);
        Integer numCount = baseMapper.selectSortingCountByCommunity(date, regionId);
        if(numCount == null) {
            numCount = 0;
        }
        statisticsVo.setTotalNum(numCount.toString());
        Integer sumByDate = sortingService.getSumByDateAndCommunity(date,community);
        if(sumByDate == null) {
            sumByDate = 0;
        }
        statisticsVo.setFinishedNumSum(String.valueOf(sumByDate));
        if (sumByDate == 0 || numCount == 0){
            statisticsVo.setPercent(BigDecimal.ZERO);
        } else {
            BigDecimal pec = BigDecimal.valueOf(sumByDate).divide(BigDecimal.valueOf(numCount),2, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100));
            pec = pec.compareTo(BigDecimal.valueOf(100)) > 0 ? BigDecimal.valueOf(100) : pec;
            statisticsVo.setPercent(pec);
        }
        return statisticsVo;
    }

    @Override
    public SortingCommunityMapVo getCommunityByDate(String community, String date) {
        HashMap<String, String> map = new HashMap<>(32);
        String communityNo = regionService.getRegionIdByName(community);
        SysDictData sysDictData = new SysDictData();
        sysDictData.setDictType(SortingDirtConstant.SORTING);
        List<SysDictData> data = sysDictDataService.selectDictDataList(sysDictData);
        List<SortingTypeBo> sortingTypeBos = baseMapper.getGarbageSortingCommunityCount(communityNo);
        buildLampTypeMap(data,sortingTypeBos,map);
        SortingCommunityMapVo sortingCommunityMapVo = new SortingCommunityMapVo();
        sortingCommunityMapVo.setSortingTypeMap(map);
        ToolStatisticsBo toolCount = toolService.getCountByServiceTypeAndCommunity(ServiceTypeConstant.SORTING, communityNo);
        sortingCommunityMapVo.setToolCount(toolCount.getToolCount());
        sortingCommunityMapVo.setVehicleCount(toolCount.getVehicleCount());
        int workmanCount = workmanService.getCommunityByServiceType(ServiceTypeConstant.SORTING, communityNo);
        sortingCommunityMapVo.setWorkmanCommunity(workmanCount);
        return sortingCommunityMapVo;
    }

    @Override
    public Integer getSortingByDateAndCommunity(String date, String community) {
        Integer sortingCount;
        if (StringUtils.isNotEmpty(community)) {
            String regionIdByName = regionService.getRegionIdByName(community);
            sortingCount = baseMapper.selectSortingCountByCommunity(date, regionIdByName);
        } else {
            sortingCount = baseMapper.selectSortingCountByCommunity(date, null);
        }
        if (null == sortingCount) sortingCount = 0;
        return sortingCount;
    }

    /**
     * 组装数据
     * @param data
     * @param sortingTypeBos
     * @param map
     */
    private void buildLampTypeMap(List<SysDictData> data, List<SortingTypeBo> sortingTypeBos, HashMap<String, String> map){
        ArrayList<String> typeName = new ArrayList<>();
        for (SysDictData lampType : data) {
            if (sortingTypeBos.size() > 0) {
                for (SortingTypeBo sortingTypeBo: sortingTypeBos) {
                    if (lampType.getDictValue().equals(sortingTypeBo.getSupervisionType())) {
                        typeName.add(lampType.getDictLabel());
                        map.put(lampType.getDictLabel(),sortingTypeBo.getSortingTypeCount() + " ");
                    }
                }
            } else {
                map.put(lampType.getDictLabel(),"0");
            }
        }
        if (typeName.size() > 0) {
            List<String> labelList = data.stream().map(SysDictData::getDictLabel).collect(Collectors.toList());
            labelList.removeAll(typeName);
            labelList.forEach(e->{
                map.put(e,"0");
            });
        }
    }

}
